#include <cassert>
#include <iostream>
#include "Epoller.hpp"
#include <sys/epoll.h>
#include <string>
#include "err.hpp"
#include "log.hpp"
#include "Sock.hpp"
using namespace std;
static const int N = 128;
class EpollServer
{
public:
    EpollServer(uint16_t port = 8888)
        : _port(port)
    {
    }

    void Init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        _epoller.Create();
    }

    void Start()
    {
        //先将我们的listen先进行等待起来
        bool n = _epoller.AddEvent(_listensock.GetSock(), EPOLLIN);
       
        assert(n);
        (void)n;
        while (true)
        {
            int timeout = -1; // 阻塞
            int n = _epoller.Wait(eparray, N, timeout);
            switch (n)
            {
            case -1:
                LogMessage(Warning, "epoll wait fail...errno:%d,errorstring:%s", errno, strerror(errno));
                break;
            case 0:
                LogMessage(Warning, "epoll timeout...");
                break;
            default:
                LogMessage(Debug, "有%d个事件就绪了", n);
                HandleEvents(n);
                break;
            }
        }
    }
    void HandleEvents(int n)
    {
        //不需要将整个eparray都遍历一遍，因为我们的输出的参数只会将就绪的拷贝出来
        for (int i = 0; i < n; i++)
        {
            if (eparray[i].events & EPOLLIN)
            {
                if (eparray[i].data.fd == _listensock.GetSock())
                {
                    // 1.处理新连接到来
                    string clientip;
                    uint16_t clientport;
                    int sock = _listensock.Accept(clientip, clientport);
                    if (sock < 0)
                    {
                        continue;
                    }
                    LogMessage(Debug, "%s:%d已经连上服务器了", clientip.c_str(), clientport);
                    bool r = _epoller.AddEvent(sock, EPOLLIN);
                    assert(r);
                    (void)r;
                }
                else
                {
                    // 2.读取
                    //这就是在epoll_ctl增加时为什么要传递fd的原因，这边可以直接使用
                    int fd = eparray[i].data.fd;
                    char buff[1024];
                    //只执行一次读取，LT工作模式，即使底层数据没读完，下一次epoll_wait还会通知
                    int n = read(fd, buff, sizeof(buff) - 1); // 读取会被阻塞吗？不会
                    if (n > 0)
                    {
                        //这边-2的原因是使用telnet链接，telnet会将换行也会传递过来，并且传递的换行时\t\n，所以说需要-2,找到\t的位置添加\0
                        buff[n-2] = 0;
                        cout << "client echo:" << buff << endl;
                        string send_to = buff;
                        send_to += "[epoll server echo]\n";//基于它的telnet的特性，换行也就传递\r\n（经过测试\n也可以）
                        cout << send_to << endl;
                        write(fd, send_to.c_str(), send_to.size());
                    }
                    else
                    {
                        if (n == 0)
                        {
                            LogMessage(Information, "client want to end...");
                        }
                        else
                        {
                            LogMessage(Warning, "read error,errno:%d,string:%s", errno, strerror(errno));
                        }
                        // 在处理异常的时候，先从epoll中移除，然后再关闭，否则fd就是不合法的
                        _epoller.DelEvent(fd);
                        close(fd); // 不能忘记，remember
                    }
                }
            }
        }
    }

private:
    uint16_t _port;
    Sock _listensock;
    Epoller _epoller;
    struct epoll_event eparray[N];//存放每次就绪的事件
};